home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / FTP / Mirror2.3 / ftp.pl < prev    next >
Encoding:
Perl Script  |  1994-01-25  |  24.0 KB  |  1,275 lines

  1. #-*-perl-*-
  2. # This is a wrapper to the chat2.pl routines that make life easier
  3. # to do ftp type work.
  4. # Mostly by Lee McLoughlin <lmjm@doc.ic.ac.uk>
  5. # based on original version by Alan R. Martello <al@ee.pitt.edu>
  6. # And by A.Macpherson@bnr.co.uk for multi-homed hosts
  7. #
  8. # Basic usage:
  9. #  $ftp_port = 21;
  10. #  $retry_call = 1;
  11. #  $attempts = 2;
  12. #  if( &ftp'open( $site, $ftp_port, $retry_call, $attempts ) != 1 ){
  13. #   die "failed to open ftp connection";
  14. #  }
  15. #  if( ! &ftp'login( $user, $pass ) ){
  16. #   die "failed to login";
  17. #  }
  18. #  &ftp'type( $text_mode ? 'A' : 'I' );
  19. #  if( ! &ftp'get( $remote_filename, $local_filename, 0 ) ){
  20. #   die "failed to get file;
  21. #  }
  22. #  &ftp'quit();
  23. #
  24. #
  25. # $Id: ftp.pl,v 2.4 1994/01/26 14:59:07 lmjm Exp lmjm $
  26. # $Log: ftp.pl,v $
  27. # Revision 2.4  1994/01/26  14:59:07  lmjm
  28. # Added DG result code.
  29. #
  30. # Revision 2.3  1994/01/18  21:58:18  lmjm
  31. # Reduce calls to sigset.
  32. # Reset to old signal after use.
  33. #
  34. # Revision 2.2  1993/12/14  11:09:06  lmjm
  35. # Use installed socket.ph.
  36. # Allow for more returns.
  37. #
  38. # Revision 2.1  1993/06/28  15:02:00  lmjm
  39. # Full 2.1 release
  40. #
  41. #
  42.  
  43. require 'sys/socket.ph';
  44. require 'chat2.pl';
  45.  
  46.  
  47. package ftp;
  48.  
  49. $retry_pause = 60;    # Pause before retrying a login.
  50.  
  51. if( defined( &main'PF_INET ) ){
  52.     $pf_inet = &main'PF_INET;
  53.     $sock_stream = &main'SOCK_STREAM;
  54.     local($name, $aliases, $proto) = getprotobyname( 'tcp' );
  55.     $tcp_proto = $proto;
  56. }
  57. else {
  58.     # XXX hardwired $PF_INET, $SOCK_STREAM, 'tcp'
  59.     # but who the heck would change these anyway? (:-)
  60.     $pf_inet = 2;
  61.     $sock_stream = 1;
  62.     $tcp_proto = 6;
  63. }
  64.  
  65. # If the remote ftp daemon doesn't respond within this time presume its dead
  66. # or something.
  67. $timeout = 120;
  68.  
  69. # Timeout a read if I don't get data back within this many seconds
  70. $timeout_read = 3 * $timeout;
  71.  
  72. # Timeout an open
  73. $timeout_open = $timeout;
  74.  
  75. $ftp'version = '$Revision: 2.4 $';
  76.  
  77. # This is a "global" it contains the last response from the remote ftp server
  78. # for use in error messages
  79. $ftp'response = "";
  80. # Also ftp'NS is the socket containing the data coming in from the remote ls
  81. # command.
  82.  
  83. # The size of block to be read or written when talking to the remote
  84. # ftp server
  85. $ftp'ftpbufsize = 4096;
  86.  
  87. # How often to print a hash out, when debugging
  88. $ftp'hashevery = 1024;
  89. # Output a newline after this many hashes to prevent outputing very long lines
  90. $ftp'hashnl = 70;
  91.  
  92. # Is there a connection open?
  93. $ftp'service_open = 0;
  94.  
  95. # If a proxy connection then who am I really talking to?
  96. $real_site = "";
  97.  
  98. # Where error/log reports are sent to
  99. $ftp'showfd = 'STDERR';
  100.  
  101. # Name of a function to call on a pathname to map it into a remote
  102. # pathname.
  103. $ftp'mapunixout = '';
  104. $ftp'manunixin = '';
  105.  
  106. # This is just a tracing aid.
  107. $ftp_show = 0;
  108.  
  109. sub ftp'debug
  110. {
  111.     $ftp_show = @_[0];
  112.     if( $ftp_show > 9 ){
  113.         $chat'debug = 1;
  114.     }
  115. }
  116.  
  117. sub ftp'set_timeout
  118. {
  119.     local( $to ) = @_;
  120.     return if $to == $timeout;
  121.     $timeout = $to;
  122.     $timeout_open = $timeout;
  123.     $timeout_read = 3 * $timeout;
  124.     if( $ftp_show ){
  125.         print $ftp'showfd "ftp timeout set to $timeout\n";
  126.     }
  127. }
  128.  
  129.  
  130. sub ftp'open_alarm
  131. {
  132.     die "timeout: open";
  133. }
  134.  
  135. sub ftp'timed_open
  136. {
  137.     local( $site, $ftp_port, $retry_call, $attempts ) = @_;
  138.     local( $connect_site, $connect_port );
  139.     local( $res );
  140.  
  141.     alarm( $timeout_open );
  142.  
  143.     while( $attempts-- ){
  144.         if( $ftp_show ){
  145.             print $ftp'showfd "proxy connecting via $proxy_gateway [$proxy_ftp_port]\n" if $proxy;
  146.             print $ftp'showfd "Connecting to $site";
  147.             if( $ftp_port != 21 ){
  148.                 print $ftp'showfd " [port $ftp_port]";
  149.             }
  150.             print $ftp'showfd "\n";
  151.         }
  152.         
  153.         if( $proxy ) {
  154.             if( ! $proxy_gateway ) {
  155.                 # if not otherwise set
  156.                 $proxy_gateway = "internet-gateway";
  157.             }
  158.             if( $debug ) {
  159.                 print $ftp'showfd "using proxy services of $proxy_gateway, ";
  160.                 print $ftp'showfd "at $proxy_ftp_port\n";
  161.             }
  162.             $connect_site = $proxy_gateway;
  163.             $connect_port = $proxy_ftp_port;
  164.             $real_site = $site;
  165.         }
  166.         else {
  167.             $connect_site = $site;
  168.             $connect_port = $ftp_port;
  169.         }
  170.         if( ! &chat'open_port( $connect_site, $connect_port ) ){
  171.             if( $retry_call ){
  172.                 print $ftp'showfd "Failed to connect\n" if $ftp_show;
  173.                 next;
  174.             }
  175.             else {
  176.                 print $ftp'showfd "proxy connection failed " if $proxy;
  177.                 print $ftp'showfd "Cannot open ftp to $connect_site\n" if $ftp_show;
  178.                 return 0;
  179.             }
  180.         }
  181.         $res = &ftp'expect( $timeout,
  182.             120, 0, # service unavailable to $site
  183.             220, 1, # ready for login to $site
  184.             421, 0); #service unavailable to $site closing connection
  185.         if( ! $res ){
  186.             &chat'close();
  187.             next;
  188.         }
  189.         return 1;
  190.     }
  191.     continue {
  192.         print $ftp'showfd "Pausing between retries\n";
  193.         sleep( $retry_pause );
  194.     }
  195.     return 0;
  196. }
  197.  
  198. sub main'ftp__sighandler
  199. {
  200.     local( $sig ) = @_;
  201.     local( $msg ) = "Caught a SIG$sig flagging connection down";
  202.     $ftp'service_open = 0;
  203.     if( $ftp_logger ){
  204.         eval "&$ftp_logger( \$msg )";
  205.     }
  206. }
  207.  
  208. sub ftp'set_signals
  209. {
  210.     $ftp_logger = @_;
  211.     $SIG{ 'PIPE' } = "ftp__sighandler";
  212. }
  213.  
  214. # Set the mapunixout and mapunixin functions
  215. sub ftp'set_namemap
  216. {
  217.     ($ftp'mapunixout, $ftp'mapunixin) = @_;
  218.     if( $debug ) {
  219.         print $ftp'showfd "mapunixout = $ftp'mapunixout, $mapunixin = $ftp'mapunixin\n";
  220.     }
  221. }
  222.  
  223.  
  224. sub ftp'open
  225. {
  226.     local( $site, $ftp_port, $retry_call, $attempts ) = @_;
  227.  
  228.     local( $old_sig ) = $SIG{ 'ALRM' };
  229.     $SIG{ 'ALRM' } = "ftp\'open_alarm";
  230.  
  231.     local( $ret ) = eval "&timed_open( '$site', $ftp_port, $retry_call, $attempts )";
  232.     alarm( 0 );
  233.     $SIG{ 'ALRM' } = $old_sig;
  234.  
  235.     if( $@ =~ /^timeout/ ){
  236.         return -1;
  237.     }
  238.  
  239.     if( $ret ){
  240.         $ftp'service_open = 1;
  241.     }
  242.  
  243.     return $ret;
  244. }
  245.  
  246. sub ftp'login
  247. {
  248.     local( $remote_user, $remote_password ) = @_;
  249.         local( $ret );
  250.  
  251.     if( ! $ftp'service_open ){
  252.         return 0;
  253.     }
  254.  
  255.     if( $proxy ){
  256.         &ftp'send( "USER $remote_user@$site" );
  257.     }
  258.     else {
  259.         &ftp'send( "USER $remote_user" );
  260.     }
  261.     $ret = &ftp'expect( $timeout,
  262.         230, 1, # $remote_user logged in
  263.         331, 2, # send password for $remote_user
  264.  
  265.         500, 0, # syntax error
  266.         501, 0, # syntax error
  267.         530, 0, # not logged in
  268.         332, 0, # account for login not supported
  269.  
  270.         421, 99 ); # service unavailable, closing connection
  271.     if( $ret == 99 ){
  272.         &service_closed();
  273.         $ret = 0;
  274.     }
  275.     if( $ret == 1 ){
  276.         # Logged in no password needed
  277.         return 1;
  278.     }
  279.     elsif( $ret == 2 ){
  280.         # A password is needed
  281.         &ftp'send( "PASS $remote_password" );
  282.  
  283.         $ret = &ftp'expect( $timeout,
  284.             230, 1, # $remote_user logged in
  285.  
  286.             202, 0, # command not implemented
  287.             332, 0, # account for login not supported
  288.  
  289.             530, 0, # not logged in
  290.             500, 0, # syntax error
  291.             501, 0, # syntax error
  292.             503, 0,  # bad sequence of commands
  293.  
  294.             421, 99 ); # service unavailable, closing connection
  295.         if( $ret == 99 ){
  296.             &service_closed();
  297.             $ret = 0;
  298.         }
  299.         if( $ret == 1 ){
  300.             # Logged in
  301.             return 1;
  302.         }
  303.     }
  304.     # If I got here I failed to login
  305.     return 0;
  306. }
  307.  
  308. sub service_closed
  309. {
  310.     $ftp'service_open = 0;
  311.     &chat'close();
  312. }
  313.  
  314. sub ftp'close
  315. {
  316.     &ftp'quit();
  317.     $ftp'service_open = 0;
  318.     &chat'close();
  319. }
  320.  
  321. # Change directory
  322. # return 1 if successful
  323. # 0 on a failure
  324. sub ftp'cwd
  325. {
  326.     local( $dir ) = @_;
  327.     local( $ret );
  328.  
  329.     if( ! $ftp'service_open ){
  330.         return 0;
  331.     }
  332.  
  333.     if( $ftp'mapunixout ){
  334.         $dir = eval "&$ftp'mapunixout( \$dir, 'd' )";
  335.     }
  336.  
  337.     &ftp'send( "CWD $dir" );
  338.  
  339.     $ret = &ftp'expect( $timeout,
  340.         200, 1, # working directory = $dir
  341.         250, 1, # working directory = $dir
  342.         257, 1, # working directory = $dir
  343.  
  344.         500, 0, # syntax error
  345.         501, 0, # syntax error
  346.                 502, 0, # command not implemented
  347.         530, 0, # not logged in
  348.                 550, 0, # cannot change directory
  349.         421, 99 ); # service unavailable, closing connection
  350.  
  351.     if( $ret == 99 ){
  352.         &service_closed();
  353.         $ret = 0;
  354.     }
  355.  
  356.     return $ret;
  357. }
  358.  
  359. # Get a full directory listing:
  360. # &ftp'dir( remote LIST options )
  361. # Start a list going with the given options.
  362. # Presuming that the remote deamon uses the ls command to generate the
  363. # data to send back then then you can send it some extra options (eg: -lRa)
  364. # return 1 if sucessful and 0 on a failure
  365. sub ftp'dir_open
  366. {
  367.     local( $options ) = @_;
  368.     local( $ret );
  369.     
  370.     if( ! $ftp'service_open ){
  371.         return 0;
  372.     }
  373.  
  374.     if( ! &ftp'open_data_socket() ){
  375.         return 0;
  376.     }
  377.     
  378.     if( $options ){
  379.         &ftp'send( "LIST $options" );
  380.     }
  381.     else {
  382.         &ftp'send( "LIST" );
  383.     }
  384.     
  385.     $ret = &ftp'expect( $timeout,
  386.         150, 1, # reading directory
  387.     
  388.         125, 0, # data connection already open?
  389.     
  390.         450, 0, # file unavailable
  391.         500, 0, # syntax error
  392.         501, 0, # syntax error
  393.         502, 0, # command not implemented
  394.         530, 0, # not logged in
  395.     
  396.             421, 99 ); # service unavailable, closing connection
  397.     if( $ret == 99 ){
  398.         &service_closed();
  399.         $ret = 0;
  400.     }
  401.  
  402.     if( ! $ret ){
  403.         &ftp'close_data_socket;
  404.         return 0;
  405.     }
  406.     
  407.     accept( NS, S ) || die "accept failed $!";
  408.  
  409.     # 
  410.     # the data should be coming at us now
  411.     #
  412.     
  413.     return 1;
  414. }
  415.  
  416.  
  417. # Close down reading the result of a remote ls command
  418. # return 1 if successful and 0 on failure
  419. sub ftp'dir_close
  420. {
  421.     local( $ret );
  422.  
  423.     if( ! $ftp'service_open ){
  424.         return 0;
  425.     }
  426.  
  427.     # read the close
  428.     #
  429.     $ret = &ftp'expect($timeout,
  430.             226, 1, # transfer complete, closing connection
  431.             250, 1, # action completed
  432.  
  433.             425, 0, # can't open data connection
  434.             426, 0, # connection closed, transfer aborted
  435.             451, 0, # action aborted, local error
  436.             421, 99 ); # service unavailable, closing connection
  437.     if( $ret == 99 ){
  438.         &service_closed();
  439.         $ret = 0;
  440.     }
  441.  
  442.     # shut down our end of the socket
  443.     &ftp'close_data_socket;
  444.  
  445.     if( ! $ret ){
  446.         return 0;
  447.     }
  448.  
  449.     return 1;
  450. }
  451.  
  452. # Quit from the remote ftp server
  453. # return 1 if successful and 0 on failure
  454. sub ftp'quit
  455. {
  456.     local( $ret );
  457.  
  458.     $site_command_check = 0;
  459.     @site_command_list = ();
  460.  
  461.     if( ! $ftp'service_open ){
  462.         return 0;
  463.     }
  464.  
  465.     &ftp'send( "QUIT" );
  466.  
  467.     $ret = &ftp'expect( $timeout, 
  468.         221, 1, # transfer complete, closing connection
  469.         500, 0, # error quitting??
  470.         421, 99 ); # service unavailable, closing connection
  471.     if( $ret == 99 ){
  472.         &service_closed();
  473.         $ret = 0;
  474.     }
  475.     return $ret;
  476. }
  477.  
  478. # Support for ftp'read
  479. sub ftp'read_alarm
  480. {
  481.     die "timeout: read";
  482. }
  483.  
  484. # Support for ftp'read
  485. sub ftp'timed_read
  486. {
  487.     alarm( $timeout_read );
  488.  
  489.     return sysread( NS, $ftpbuf, $ftpbufsize );
  490. }
  491.  
  492. # Do not use this routing use ftp'get
  493. sub ftp'read
  494. {
  495.     if( ! $ftp'service_open ){
  496.         return -1;
  497.     }
  498.  
  499.     local( $ret ) = eval '&timed_read()';
  500.     alarm( 0 );
  501.  
  502.     if( $@ =~ /^timeout/ ){
  503.         return -1;
  504.     }
  505.     return $ret;
  506. }
  507.  
  508. # Get a remote file back into a local file.
  509. # If no loc_fname passed then uses rem_fname.
  510. # returns 1 on success and 0 on failure
  511. sub ftp'get
  512. {
  513.     local($rem_fname, $loc_fname, $restart ) = @_;
  514.     local( $ret );
  515.     
  516.     if( ! $ftp'service_open ){
  517.         return 0;
  518.     }
  519.  
  520.     if( $loc_fname eq "" ){
  521.         $loc_fname = $rem_fname;
  522.     }
  523.     
  524.     if( ! &ftp'open_data_socket() ){
  525.         print $ftp'showfd "Cannot open data socket\n";
  526.         return 0;
  527.     }
  528.  
  529.     if( $loc_fname ne '-' ){
  530.         # Find the size of the target file
  531.         local( $restart_at ) = &ftp'filesize( $loc_fname );
  532.         if( $restart && $restart_at > 0 && &ftp'restart( $restart_at ) ){
  533.             $restart = 1;
  534.             # Make sure the file can be updated
  535.             chmod( 0644, $loc_fname );
  536.         }
  537.         else {
  538.             $restart = 0;
  539.             unlink( $loc_fname );
  540.         }
  541.     }
  542.  
  543.     if( $ftp'mapunixout ){
  544.         $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
  545.     }
  546.  
  547.     &ftp'send( "RETR $rem_fname" );
  548.     
  549.     $ret = &ftp'expect( $timeout, 
  550.         150, 1, # receiving $rem_fname
  551.  
  552.         125, 0, # data connection already open?
  553.         450, 2, # file unavailable
  554.         550, 2, # file unavailable
  555.         500, 0, # syntax error
  556.         501, 0, # syntax error
  557.         530, 0, # not logged in
  558.  
  559.         421, 99 ); # service unavailable, closing connection
  560.     if( $ret == 99 ){
  561.         &service_closed();
  562.         $ret = 0;
  563.     }
  564.     if( $ret != 1 ){
  565.         print $ftp'showfd "Failure on 'RETR $rem_fname' command\n";
  566.  
  567.         # shut down our end of the socket
  568.         &ftp'close_data_socket;
  569.  
  570.         return 0;
  571.     }
  572.  
  573.     accept( NS, S ) || die "accept failed $!";
  574.  
  575.     # 
  576.     # the data should be coming at us now
  577.     #
  578.  
  579.     #
  580.     #  open the local fname
  581.     #  concatenate on the end if restarting, else just overwrite
  582.     if( !open( FH, ($restart ? '>>' : '>') . $loc_fname ) ){
  583.         print $ftp'showfd "Cannot create local file $loc_fname\n";
  584.  
  585.         # shut down our end of the socket
  586.         &ftp'close_data_socket;
  587.  
  588.         return 0;
  589.     }
  590.  
  591.     local( $start_time ) = time;
  592.     local( $bytes, $lasthash, $hashes ) = (0, 0, 0);
  593.     local( $old_sig ) = $SIG{ 'ALRM' };
  594.     $SIG{ 'ALRM' } = "ftp\'read_alarm";
  595.     while( ($len = &ftp'read()) > 0 ){
  596.         $bytes += $len;
  597.         if( $strip_cr ){
  598.             $ftp'buf =~ s/\r//g;
  599.         }
  600.         if( $ftp_show ){
  601.             while( $bytes > ($lasthash + $ftp'hashevery) ){
  602.                 print $ftp'showfd '#';
  603.                 $lasthash += $ftp'hashevery;
  604.                 $hashes++;
  605.                 if( ($hashes % $ftp'hashnl) == 0 ){
  606.                     print $ftp'showfd "\n";
  607.                 }
  608.             }
  609.         }
  610.         if( ! print FH $ftp'ftpbuf ){
  611.             print $ftp'showfd "\nfailed to write data";
  612.             $bytes = -1;
  613.             last;
  614.         }
  615.     }
  616.     $SIG{ 'ALRM' } = $old_sig;
  617.     close( FH );
  618.  
  619.     # shut down our end of the socket
  620.     &ftp'close_data_socket;
  621.  
  622.     if( $len < 0 ){
  623.         print $ftp'showfd "\ntimed out reading data!\n";
  624.  
  625.         return 0;
  626.     }
  627.         
  628.     if( $ftp_show && $bytes > 0 ){
  629.         if( $hashes && ($hashes % $ftp'hashnl) != 0 ){
  630.             print $ftp'showfd "\n";
  631.         }
  632.         local( $secs ) = (time - $start_time);
  633.         if( $secs <= 0 ){
  634.             $secs = 1; # To avoid a divide by zero;
  635.         }
  636.  
  637.         local( $rate ) = int( $bytes / $secs );
  638.         print $ftp'showfd "Got $bytes bytes ($rate bytes/sec)\n";
  639.     }
  640.  
  641.     #
  642.     # read the close
  643.     #
  644.  
  645.     $ret = &ftp'expect( $timeout, 
  646.         226, 1, # transfer complete, closing connection
  647.             250, 1, # action completed
  648.     
  649.             110, 0, # restart not supported
  650.             425, 0, # can't open data connection
  651.             426, 0, # connection closed, transfer aborted
  652.             451, 0, # action aborted, local error
  653.         550, 0, # permission denied
  654.  
  655.         421, 99 ); # service unavailable, closing connection
  656.     if( $ret == 99 ){
  657.         &service_closed();
  658.         $ret = 0;
  659.     }
  660.  
  661.     if( $ret && $bytes < 0 ){
  662.         $ret = 0;
  663.     }
  664.  
  665.     return $ret;
  666. }
  667.  
  668. sub ftp'delete
  669. {
  670.     local( $rem_fname ) = @_;
  671.     local( $ret );
  672.  
  673.     if( ! $ftp'service_open ){
  674.         return 0;
  675.     }
  676.  
  677.     if( $ftp'mapunixout ){
  678.         $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
  679.     }
  680.  
  681.     &ftp'send( "DELE $rem_fname" );
  682.  
  683.     $ret = &ftp'expect( $timeout, 
  684.         250, 1, # Deleted $rem_fname
  685.         550, 0, # Permission denied
  686.  
  687.         421, 99 ); # service unavailable, closing connection
  688.     if( $ret == 99 ){
  689.         &service_closed();
  690.         $ret = 0;
  691.     }
  692.  
  693.     return $ret == 1;
  694. }
  695.  
  696. sub ftp'deldir
  697. {
  698.     local( $fname ) = @_;
  699.  
  700.     # not yet implemented
  701.     # RMD
  702. }
  703.  
  704. # UPDATE ME!!!!!!
  705. # Add in the hash printing and newline conversion
  706. sub ftp'put
  707. {
  708.     local( $loc_fname, $rem_fname ) = @_;
  709.     local( $strip_cr );
  710.     
  711.     if( ! $ftp'service_open ){
  712.         return 0;
  713.     }
  714.  
  715.     if( $loc_fname eq "" ){
  716.         $loc_fname = $rem_fname;
  717.     }
  718.     
  719.     if( ! &ftp'open_data_socket() ){
  720.         return 0;
  721.     }
  722.     
  723.     if( $ftp'mapunixout ){
  724.         $rem_fname = eval "&$ftp'mapunixout( \$rem_fname, 'f' )";
  725.     }
  726.  
  727.     &ftp'send( "STOR $rem_fname" );
  728.     
  729.     # 
  730.     # the data should be coming at us now
  731.     #
  732.     
  733.     local( $ret ) =
  734.     &ftp'expect( $timeout, 
  735.         150, 1, # sending $loc_fname
  736.  
  737.         125, 0, # data connection already open?
  738.         450, 0, # file unavailable
  739.         532, 0, # need account for storing files
  740.         452, 0, # insufficient storage on system
  741.         553, 0, # file name not allowed
  742.         500, 0, # syntax error
  743.         501, 0, # syntax error
  744.         530, 0, # not logged in
  745.  
  746.         421, 99 ); # service unavailable, closing connection
  747.     if( $ret == 99 ){
  748.         &service_closed();
  749.         $ret = 0;
  750.     }
  751.  
  752.     if( $ret != 1 ){
  753.         # shut down our end of the socket
  754.         &ftp'close_data_socket;
  755.  
  756.         return 0;
  757.     }
  758.  
  759.  
  760.     accept( NS, S ) || die "accept failed $!";
  761.  
  762.     # 
  763.     # the data should be coming at us now
  764.     #
  765.     
  766.     #
  767.     #  open the local fname
  768.     #
  769.     if( !open( FH, "<$loc_fname" ) ){
  770.         print $ftp'showfd "Cannot open local file $loc_fname\n";
  771.  
  772.         # shut down our end of the socket
  773.         &ftp'close_data_socket;
  774.  
  775.         return 0;
  776.     }
  777.     
  778.     while( <FH> ){
  779.         if( ! $ftp'service_open ){
  780.             last;
  781.         }
  782.         print NS ;
  783.     }
  784.     close( FH );
  785.     
  786.     # shut down our end of the socket to signal EOF
  787.     &ftp'close_data_socket;
  788.     
  789.     #
  790.     # read the close
  791.     #
  792.     
  793.     $ret = &ftp'expect( $timeout, 
  794.         226, 1, # transfer complete, closing connection
  795.         250, 1, # action completed
  796.     
  797.         110, 0, # restart not supported
  798.         425, 0, # can't open data connection
  799.         426, 0, # connection closed, transfer aborted
  800.         451, 0, # action aborted, local error
  801.         551, 0, # page type unknown
  802.         552, 0, # storage allocation exceeded
  803.     
  804.         421, 99 ); # service unavailable, closing connection
  805.     if( $ret == 99 ){
  806.         &service_closed();
  807.         $ret = 0;
  808.     }
  809.     if( ! $ret ){
  810.         print $ftp'showfd "Failure on 'STOR $loc_fname' command\n";
  811.     }
  812.     return $ret;
  813. }
  814.  
  815. sub ftp'restart
  816. {
  817.     local( $restart_point, $ret ) = @_;
  818.  
  819.     if( ! $ftp'service_open ){
  820.         return 0;
  821.     }
  822.  
  823.     &ftp'send( "REST $restart_point" );
  824.  
  825.     # 
  826.     # see what they say
  827.  
  828.     $ret = &ftp'expect( $timeout, 
  829.         350, 1, # restarting at $restart_point
  830.                
  831.         500, 0, # syntax error
  832.         501, 0, # syntax error
  833.         502, 2, # REST not implemented
  834.         530, 0, # not logged in
  835.         545, 2, # REST not implemented
  836.         554, 2, # REST not implemented
  837.                
  838.         421, 99 ); # service unavailable, closing connection
  839.     if( $ret == 99 ){
  840.         &service_closed();
  841.         $ret = 0;
  842.     }
  843.     return $ret;
  844. }
  845.  
  846. # Set the file transfer type
  847. sub ftp'type
  848. {
  849.     local( $type ) = @_;
  850.  
  851.     if( ! $ftp'service_open ){
  852.         return 0;
  853.     }
  854.  
  855.     &ftp'send( "TYPE $type" );
  856.  
  857.     # 
  858.     # see what they say
  859.  
  860.     $ret = &ftp'expect( $timeout, 
  861.         200, 1, # file type set to $type
  862.                
  863.         500, 0, # syntax error
  864.         501, 0, # syntax error
  865.         504, 0, # Invalid form or byte size for type $type
  866.                
  867.         421, 99 ); # service unavailable, closing connection
  868.     if( $ret == 99 ){
  869.         &service_closed();
  870.         $ret = 0;
  871.     }
  872.     return $ret;
  873. }
  874.  
  875. $site_command_check = 0;
  876. @site_command_list = ();
  877.  
  878. # routine to query the remote server for 'SITE' commands supported
  879. sub ftp'site_commands
  880. {
  881.     local( $ret );
  882.     
  883.     if( ! $ftp'service_open ){
  884.         return 0;
  885.     }
  886.  
  887.     # if we havent sent a 'HELP SITE', send it now
  888.     if( !$site_command_check ){
  889.     
  890.         $site_command_check = 1;
  891.     
  892.         &ftp'send( "HELP SITE" );
  893.     
  894.         # assume the line in the HELP SITE response with the 'HELP'
  895.         # command is the one for us
  896.         $ret = &ftp'expect( $timeout,
  897.             ".*HELP.*", "\$1",
  898.             214, "0",
  899.             202, "0",
  900.             421, "99" ); # service unavailable, closing connection
  901.         if( $ret == 99 ){
  902.             &service_closed();
  903.             $ret = "0";
  904.         }
  905.     
  906.         if( $ret eq "0" ){
  907.             print $ftp'showfd "No response from HELP SITE\n" if( $ftp_show );
  908.         }
  909.     
  910.         @site_command_list = split(/\s+/, $ret);
  911.     }
  912.     
  913.     return @site_command_list;
  914. }
  915.  
  916. # return the pwd, or null if we can't get the pwd
  917. sub ftp'pwd
  918. {
  919.     local( $ret, $cwd );
  920.  
  921.     if( ! $ftp'service_open ){
  922.         return 0;
  923.     }
  924.  
  925.     &ftp'send( "PWD" );
  926.  
  927.     # 
  928.     # see what they say
  929.  
  930.     $ret = &ftp'expect( $timeout, 
  931.         257, 1, # working dir is
  932.         500, 0, # syntax error
  933.         501, 0, # syntax error
  934.         502, 0, # PWD not implemented
  935.         550, 0, # file unavailable
  936.  
  937.         421, 99 ); # service unavailable, closing connection
  938.     if( $ret == 99 ){
  939.         &service_closed();
  940.         $ret = 0;
  941.     }
  942.     if( $ret ){
  943.         if( $ftp'response =~ /^257\s"(.*)"\s.*$/ ){
  944.             $cwd = $1;
  945.         }
  946.     }
  947.     return $cwd;
  948. }
  949.  
  950. # return 1 for success, 0 for failure
  951. sub ftp'mkdir
  952. {
  953.     local( $path ) = @_;
  954.     local( $ret );
  955.  
  956.     if( ! $ftp'service_open ){
  957.         return 0;
  958.     }
  959.  
  960.     if( $ftp'mapunixout ){
  961.         $path = eval "&$ftp'mapunixout( \$path, 'f' )";
  962.     }
  963.  
  964.     &ftp'send( "MKD $path" );
  965.  
  966.     # 
  967.     # see what they say
  968.  
  969.     $ret = &ftp'expect( $timeout, 
  970.         257, 1, # made directory $path
  971.                
  972.         500, 0, # syntax error
  973.         501, 0, # syntax error
  974.         502, 0, # MKD not implemented
  975.         530, 0, # not logged in
  976.         550, 0, # file unavailable
  977.  
  978.         421, 99 ); # service unavailable, closing connection
  979.     if( $ret == 99 ){
  980.         &service_closed();
  981.         $ret = 0;
  982.     }
  983.     return $ret;
  984. }
  985.  
  986. # return 1 for success, 0 for failure
  987. sub ftp'chmod
  988. {
  989.     local( $path, $mode ) = @_;
  990.     local( $ret );
  991.  
  992.     if( ! $ftp'service_open ){
  993.         return 0;
  994.     }
  995.  
  996.     if( $ftp'mapunixout ){
  997.         $path = eval "&$ftp'mapunixout( \$path, 'f' )";
  998.     }
  999.  
  1000.     &ftp'send( sprintf( "SITE CHMOD %o $path", $mode ) );
  1001.  
  1002.     # 
  1003.     # see what they say
  1004.  
  1005.     $ret = &ftp'expect( $timeout, 
  1006.         200, 1, # chmod $mode $path succeeded
  1007.                
  1008.         500, 0, # syntax error
  1009.         501, 0, # syntax error
  1010.         502, 0, # CHMOD not implemented
  1011.         530, 0, # not logged in
  1012.         550, 0, # file unavailable
  1013.  
  1014.         421, 99 ); # service unavailable, closing connection
  1015.     if( $ret == 99 ){
  1016.         &service_closed();
  1017.         $ret = 0;
  1018.     }
  1019.     return $ret;
  1020. }
  1021.  
  1022. # rename a file
  1023. sub ftp'rename
  1024. {
  1025.     local( $old_name, $new_name ) = @_;
  1026.     local( $ret );
  1027.  
  1028.     if( ! $ftp'service_open ){
  1029.         return 0;
  1030.     }
  1031.  
  1032.     if( $ftp'mapunixout ){
  1033.         $old_name = eval "&$ftp'mapunixout( \$old_name, 'f' )";
  1034.     }
  1035.  
  1036.     &ftp'send( "RNFR $old_name" );
  1037.  
  1038.     # 
  1039.     # see what they say
  1040.  
  1041.     $ret = &ftp'expect( $timeout, 
  1042.         350, 1, #  OK
  1043.                
  1044.         500, 0, # syntax error
  1045.         501, 0, # syntax error
  1046.         502, 0, # RNFR not implemented
  1047.         530, 0, # not logged in
  1048.         550, 0, # file unavailable
  1049.         450, 0, # file unavailable
  1050.                
  1051.         421, 99 ); # service unavailable, closing connection
  1052.     if( $ret == 99 ){
  1053.         &service_closed();
  1054.         $ret = 0;
  1055.     }
  1056.  
  1057.     # check if the "rename from" occurred ok
  1058.     if( $ret ){
  1059.         if( $ftp'mapunixout ){
  1060.             $new_name = eval "&$ftp'mapunixout( \$new_name, 'f' )";
  1061.         }
  1062.  
  1063.         &ftp'send( "RNTO $new_name" );
  1064.     
  1065.         # 
  1066.         # see what they say
  1067.     
  1068.         $ret = &ftp'expect( $timeout, 
  1069.             250, 1,  # rename $old_name to $new_name
  1070.  
  1071.             500, 0, # syntax error
  1072.             501, 0, # syntax error
  1073.             502, 0, # RNTO not implemented
  1074.             503, 0, # bad sequence of commands
  1075.             530, 0, # not logged in
  1076.             532, 0, # need account for storing files
  1077.             553, 0, # file name not allowed
  1078.  
  1079.             421, 99 ); # service unavailable, closing connection
  1080.         if( $ret == 99 ){
  1081.             &service_closed();
  1082.             $ret = 0;
  1083.         }
  1084.     }
  1085.  
  1086.     return $ret;
  1087. }
  1088.  
  1089.  
  1090. sub ftp'quote
  1091. {
  1092.     local( $cmd ) = @_;
  1093.     local( $ret );
  1094.  
  1095.     if( ! $ftp'service_open ){
  1096.         return 0;
  1097.     }
  1098.  
  1099.     &ftp'send( $cmd );
  1100.  
  1101.     $ret = &ftp'expect( $timeout, 
  1102.         200, 1, # Remote '$cmd' OK
  1103.         500, 0, # error in remote '$cmd'
  1104.         421, 99 ); # service unavailable, closing connection
  1105.     if( $ret == 99 ){
  1106.         &service_closed();
  1107.         $ret = 0;
  1108.     }
  1109.     return $ret;
  1110. }
  1111.  
  1112. # ------------------------------------------------------------------------------
  1113. # These are the lower level support routines
  1114.  
  1115. sub ftp'expectgot
  1116. {
  1117.     ($ftp'response, $ftp'fatalerror) = @_;
  1118.     if( $ftp_show ){
  1119.         print $ftp'showfd "$ftp'response\n";
  1120.     }
  1121. }
  1122.  
  1123. #
  1124. #  create the list of parameters for chat'expect
  1125. #
  1126. #  ftp'expect(time_out, {value, return value});
  1127. #  the last response is stored in $ftp'response
  1128. #
  1129. sub ftp'expect
  1130. {
  1131.     local( $ret );
  1132.     local( $time_out );
  1133.     local( @expect_args );
  1134.     local( $code, $pre );
  1135.     
  1136.     $ftp'response = '';
  1137.     $ftp'fatalerror = 0;
  1138.  
  1139.     $time_out = shift( @_ );
  1140.     
  1141.     while( @_ ){
  1142.         $code = shift( @_ );
  1143.         $pre = '^';
  1144.         if( $code =~ /^\d+$/ ){
  1145.             $pre = "[.|\n]*^";
  1146.         }
  1147.         push( @expect_args, "$pre(" . $code . " .*)\\015\\n" );
  1148.         push( @expect_args, 
  1149.             "&expectgot( \$1, 0 ); " . shift( @_ ) );
  1150.     }
  1151.     
  1152.     # Treat all unrecognised lines as continuations
  1153.     push( @expect_args, "^(.*)\\015\\n" );
  1154.     push( @expect_args, "&expectgot( \$1, 0 ); 100" );
  1155.     
  1156.     # add patterns TIMEOUT and EOF
  1157.     
  1158.     push( @expect_args, 'TIMEOUT' );
  1159.     push( @expect_args, "&expectgot( 'timed out', 0 ); 0" );
  1160.     
  1161.     push( @expect_args, 'EOF' );
  1162.     push( @expect_args, "&expectgot( 'remote server gone away', 1 ); 99" );
  1163.     
  1164.     if( $ftp_show > 9 ){
  1165.         &printargs( $time_out, @expect_args );
  1166.     }
  1167.     
  1168.     $ret = &chat'expect( $time_out, @expect_args );
  1169.     if( $ret == 100 ){
  1170.         # we saw a continuation line, wait for the end
  1171.         push( @expect_args, "^.*\n" );
  1172.         push( @expect_args, "100" );
  1173.     
  1174.         while( $ret == 100 ){
  1175.             if( $ftp_show > 9 ){
  1176.                 &printargs( $time_out, @expect_args );
  1177.             }
  1178.             $ret = &chat'expect( $time_out, @expect_args );
  1179.         }
  1180.     }
  1181.  
  1182.     return $ret;
  1183. }
  1184.  
  1185.  
  1186. #
  1187. #  opens NS for io
  1188. #
  1189. sub ftp'open_data_socket
  1190. {
  1191.     local( $sockaddr, $port );
  1192.     local( $type, $myaddr, $a, $b, $c, $d );
  1193.     local( $mysockaddr, $family, $hi, $lo );
  1194.     
  1195.     $sockaddr = 'S n a4 x8';
  1196.  
  1197.     ($a,$b,$c,$d) = unpack( 'C4', $chat'thisaddr );
  1198.     $this = $chat'thisproc;
  1199.     
  1200.     socket( S, $pf_inet, $sock_stream, $tcp_proto ) || die "socket: $!";
  1201.     bind( S, $this ) || die "bind: $!";
  1202.     
  1203.     # get the port number
  1204.     $mysockaddr = getsockname( S );
  1205.     ($family, $port, $myaddr) = unpack( $sockaddr, $mysockaddr );
  1206.     
  1207.     $hi = ($port >> 8) & 0x00ff;
  1208.     $lo = $port & 0x00ff;
  1209.     
  1210.     #
  1211.     # we MUST do a listen before sending the port otherwise
  1212.     # the PORT may fail
  1213.     #
  1214.     listen( S, 5 ) || die "listen";
  1215.     
  1216.     &ftp'send( "PORT $a,$b,$c,$d,$hi,$lo" );
  1217.     
  1218.     return &ftp'expect($timeout,
  1219.         200, 1, # PORT command successful
  1220.         250, 1, # PORT command successful
  1221.  
  1222.         500, 0, # syntax error
  1223.         501, 0, # syntax error
  1224.         530, 0, # not logged in
  1225.  
  1226.         421, 0); # service unavailable, closing connection
  1227. }
  1228.     
  1229. sub ftp'close_data_socket
  1230. {
  1231.     close( NS );
  1232. }
  1233.  
  1234. sub ftp'send
  1235. {
  1236.     local( $send_cmd ) = @_;
  1237.  
  1238.     if( $send_cmd =~ /\n/ ){
  1239.         print $ftp'showfd "ERROR, \\n in send string for $send_cmd\n";
  1240.     }
  1241.     
  1242.     if( $ftp_show ){
  1243.         local( $sc ) = $send_cmd;
  1244.  
  1245.         if( $send_cmd =~ /^PASS/){
  1246.             $sc = "PASS <somestring>";
  1247.         }
  1248.         print $ftp'showfd "---> $sc\n";
  1249.     }
  1250.     
  1251.     &chat'print( "$send_cmd\r\n" );
  1252. }
  1253.  
  1254. sub ftp'printargs
  1255. {
  1256.     while( @_ ){
  1257.         print $ftp'showfd shift( @_ ) . "\n";
  1258.     }
  1259. }
  1260.  
  1261. sub ftp'filesize
  1262. {
  1263.     local( $fname ) = @_;
  1264.  
  1265.     if( ! -f $fname ){
  1266.         return -1;
  1267.     }
  1268.  
  1269.     return (stat( _ ))[ 7 ];
  1270.     
  1271. }
  1272.  
  1273. # make this package return true
  1274. 1;
  1275.